#pragma once
#include "Log.hpp"
#include "TcpServer.hpp"
#include "Transaction.hpp"
#include "Session.hpp"
#include "Util.hpp"
#include <string>
#include <unordered_map>
#include <sstream>
#include <memory>
#include <cstring>

namespace HttpModule
{
    const static std::string blankspace = " ";
    const static std::string linebreak = "\r\n";
    const static std::string colonspace = ": ";
    const static std::string netload = "./wwwroot";

    class HttpRequest
    {
    public:
        HttpRequest() {}

        std::string Method()
        {
            return _method;
        }

        std::string Uri()
        {
            return _uri;
        }

        std::string Version()
        {
            return _version;
        }

        std::string Args()
        {
            return _args;
        }

        std::string Text()
        {
            return _text;
        }

        bool Interact()
        {
            return _is_interact;
        }

        std::string SessionId()
        {
            return _sessionid;
        }

        std::string Serialize()
        {
            return std::string();
        }

        // 将首行信息设置在HTTP请求中.
        bool ParseOneLine(std::string *req)
        {
            std::stringstream ss(*req);
            ss >> _method >> _uri >> _version;
            if (_uri == "/")
                _uri = "index.html";
            return !ss.fail();
        }

        void SetHeader(std::string key, std::string val)
        {
            // 如果有相同的key怎么计算呢？
            _reqheader.insert(std::make_pair(key, val));
        }

        void ParseHeader(std::string *req)
        {
            size_t pos;
            while ((pos = req->find(linebreak)) != std::string::npos)
            {
                // 0、如果字符串首部是"\r\n"开头，说明报头读取完毕
                if (req->substr(0, 2).c_str() == linebreak)
                {
                    req->erase(0, 2);
                    break;
                }
                // 1、找到\r\n,取出报头信息,从原来的串中删除该报头
                std::string header = req->substr(0, pos);
                req->erase(0, pos + linebreak.size());
                // 2、将报头信息区分开来
                auto colonpos = header.find(colonspace);
                std::string header_key = header.substr(0, colonpos);
                std::string header_val = header.substr(colonpos + colonspace.size());

                // 3、判断是否是Cookie,如果是放在session中，否则放在头里面。

                if (header_key == "Cookie")
                {
                    _sessionid = header_val;
                }
                else
                {
                    SetHeader(header_key, header_val);
                }
            }
            _text = req->c_str();
        }

        void DebugHeader()
        {
            for (auto &x : _reqheader)
            {
                LOG_DEBUG << x.first << colonspace.c_str() << x.second;
            }
        }

        void Debug()
        {
            LOG_DEBUG << _method << " " << _uri << " " << _version;
            DebugHeader();
            LOG_DEBUG << "_is_interact: " << _is_interact;
            LOG_DEBUG << "_args: " << _args;
            LOG_DEBUG << "_text: " << _text;
        }

        bool Deserialize(std::string req)
        {
            // 1、解析首行数据，解析后删除掉首行
            auto pos = req.find(linebreak);
            if (pos == std::string::npos)
            {
                return false;
            }
            if (ParseOneLine(&req))
            {
                _uri = netload + _uri;
            }
            else
            {
                return false;
            }
            req.erase(0, pos + linebreak.size());

            // 2、解析剩下部分的数据
            ParseHeader(&req);

            // 3、分析报文的传送方式，判断是否需要动态交互
            if (_method == "GET")
            {
                // _uri: ./wwwroot/login
                // username=zhangsan&password=123456
                // 3.1、不需要动态交互
                const std::string temp = "?";
                pos = _uri.find(temp);
                if (pos == std::string::npos)
                {
                    _is_interact = false;
                }
                else
                {
                    // 3.2、需要动态交互
                    LOG_DEBUG << "Client hope to interact by Get";
                    _args = _uri.substr(pos + temp.size());
                    _uri = _uri.substr(0, pos);
                    _is_interact = true;
                }
                return true;
            }
            else if (_method == "POST")
            {
                // 3.3、需要动态交互,数据格式需要在动态交互时候再去处理
                LOG_DEBUG << "Client hope to interact by Get";
                _is_interact = true;
            }

            LOG_DEBUG << "Deserizlize finished!";
            Debug();
            return true;
        }

    private:
        // http首行
        std::string _method;
        std::string _uri;
        std::string _version;

        // 看看头部中是否存在要带参数的问题
        std::string _args;
        bool _is_interact;

        // http请求报头
        std::unordered_map<std::string, std::string> _reqheader;

        // http请求正文
        std::string _text;

        // Sessionid
        std::string _sessionid;
    };

    class HttpResponse
    {
    public:
        HttpResponse() {}

        std::string Serialize()
        {
            std::string headerstr;
            for (auto &x : _respheader)
            {
                headerstr += x.first + colonspace + x.second + linebreak;
            }
            for (auto &x : _cookie)
            {
                headerstr += x + linebreak;
            }
            // LOG_DEBUG << "Serialize response: " << _vision + blankspace + _statuscode + blankspace + _statusdesc + linebreak + headerstr + linebreak + _text;

            return _vision + blankspace + _statuscode + blankspace + _statusdesc + linebreak + headerstr + linebreak + (!_text.empty() ? _text : "");
        }

        void Deserialize(std::string msg) {}

        void ConvertCodeStr(std::string code)
        {
            std::string codestr;
            if (code == "100")
            {
                codestr = "Continue"; // 继续上传文件
            }
            else if (code == "200")
            {
                codestr = "OK";
            }
            else if (code == "201")
            {
                codestr = "Created"; // 发布新文章成功。
            }
            else if (code == "204")
            {
                codestr = "No Content"; // 删除文章后，服务器返回表示删除成功
            }
            else if (code == "301")
            {
                codestr = "Moved Permanently"; // 永久重定向
            }
            else if (code == "302")
            {
                codestr = "See Other/Found"; // 登陆成功跳转到首页
            }
            else if (code == "400")
            {
                codestr = "Bad Request"; // 表单填写格式错误
            }
            else if (code == "401")
            {
                codestr = "Unauthorized";
            }
            else if (code == "404")
            {
                codestr = "Not Found"; // 访问不存在的连接
            }
            else if (code == "500")
            {
                codestr = "Internal Server Error"; // 服务器崩溃/数据库错误
            }
            else if (code == "503")
            {
                codestr = "Service Unavailable"; // 服务器处理不过来数据了
            }
            else
            {
                codestr = "undefined code";
            }
            _statusdesc = codestr;
        }

        // 针对的是非动态交互请求来应答，动态交互走HTTP回调逻辑。
        void MakeResponse()
        {
            // 1、查找目标文件是否存在，在的话就打开读取到_text
            bool ret = Util::ReadMessage(_targetfile, &_text);
            if (ret)
            {
                // 存在：
                // 2、计算_text的长度
                bool filestatus;
                size_t filelenth = Util::FileSize(_targetfile, &filestatus);
                if (!filestatus)
                {
                    LOG_ERROR << "Reading file failed!";
                    return;
                }

                // 3、设置状态码，状态信息
                SetStatusCode("200");
                ConvertCodeStr("200");

                // 4、设置报头信息 内容长度，文章类型
                // 4.1、设置长度
                SetHeader("Content-Length", std::to_string(filelenth));
                // 4.2、设置类型
                std::string filetype;
                if (!Util::FileType(_targetfile, &filetype))
                {
                    LOG_ERROR << "The type of the file errors!";
                    return;
                }
                SetHeader("Content-Type", filetype);
            }
            else
            {
                Error_404();
            }
        }

        void Redirect(std::string file)
        {
            // 1、不存在,设置状态码消息为302
            SetStatusCode("302");
            ConvertCodeStr("302");
            // 2、设置报头信息，Location
            std::string location = "http://192.168.146.131:8080" + file;
            SetHeader("Location", location);
            SetHeader("Content-Length", "0");
        }

        void Error_404()
        {
            Redirect("/404.html");
        }

        void IndexPage()
        {
            Redirect("/index.html");
        }

        void LoginPage()
        {
            Redirect("/login.html");
        }

        std::string GetMonthName(int month)
        {
            std::vector<std::string> months = {"Jan", "Feb", "Mar",
                                               "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
            return months[month];
        }
        std::string GetWeekDayName(int day)
        {
            std::vector<std::string> weekdays = {"Sun", "Mon", "Tue",
                                                 "Wed", "Thu", "Fri", "Sat"};
            return weekdays[day];
        }
        std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来 UTC 时间
        {
            time_t timeout = time(nullptr) + t;
            // 这个地方有坑哦
            struct tm *tm = gmtime(&timeout); // 这里不能用 localtime，
            // 因为 localtime 是默认带了时区的.gmtime 获取的就是 UTC 统一时间
            //  时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTC
            char timebuffer[1024];
            snprintf(timebuffer, sizeof(timebuffer),
                     "%s, %02d %s %d %02d:%02d:%02d UTC",
                     GetWeekDayName(tm->tm_wday).c_str(),
                     tm->tm_mday,
                     GetMonthName(tm->tm_mon).c_str(),
                     tm->tm_year + 1900,
                     tm->tm_hour,
                     tm->tm_min,
                     tm->tm_sec);
            return timebuffer;
        }

        void SetVision(std::string vision) { _vision = vision; }
        void SetStatusCode(std::string statuscode) { _statuscode = statuscode; }
        void SetStatusdesc(std::string statusdesc) { _statusdesc = statusdesc; }
        void SetHeader(std::string key, std::string val)
        {
            // 如果有相同的key怎么计算呢？
            if (key == "Set-Cookie")
            {
                std::string cookie = key + colonspace + val;
                _cookie.push_back(cookie);
            }
            else
            {
                _respheader.insert(std::make_pair(key, val));
            }
        }
        void SetTarget(std::string file) { _targetfile = file; }
        void SetText(std::string text) { _text = text; }

        void Debug()
        {
            LOG_DEBUG << "_vision: " << _vision;
            LOG_DEBUG << "_statuscode: " << _statuscode;
            LOG_DEBUG << "_statuscode: " << _statusdesc;
            for (auto &x : _respheader)
            {
                LOG_DEBUG << "first: " << x.first << " second: " << x.second;
            }
            LOG_DEBUG << "_targetfile: " << _targetfile;
        }

    private:
        // http响应报头
        std::string _vision;
        std::string _statuscode;
        std::string _statusdesc;

        // http报头信息,如果存储的是Cookie，要使用vector存储，否则会覆盖。
        std::unordered_map<std::string, std::string> _respheader;
        std::vector<std::string> _cookie;

        // http中的text
        std::string _targetfile;
        std::string _text;
    };

    class Http
    {
        using Handler_http_t = std::function<std::string(std::string)>;
        using Transaction_handler_t = std::function<std::string(std::string)>;
        using Result_hander_t = std::function<std::string(std::string)>;
        using Session_hander_t = std::function<bool(HttpRequest &)>;

    public:
        Http(uint16_t port) : _tcpserver(std::make_unique<TcpServerModule::TcpServer>(port))
        {
            // std::unique_ptr<Handler_http_t> handler 是指针，如果想指向函数指针类型
            // 需要指向make_unique包装下的类型。
            // 写法一：使用智能指针托管bind返回的对象，可用auto接收
            // std::unique_ptr<Handler_http_t> func_pptr = std::make_unique<Handler_http_t>(std::bind(&Http::HandlerRequest, this, std::placeholders::_1));
            // 写法二：直接推导bind返回值
            // auto x =  std::bind(&Http::HandlerRequest, this, std::placeholders::_1);
            // Handler_http_t func = std::bind(&Http::HandlerRequest, this, std::placeholders::_1);
            RegisterResultHandler();
            RegisterTransactionHandler();
            for (const auto &kv : _result_route)
            {
                std::cout << kv.first << std::endl;
            }
            for (const auto &kv : _trans_route)
            {
                std::cout << kv.first << std::endl;
            }
            std::cout << std::endl;

            _tcpserver->Start(std::bind(&Http::HandlerRequest, this, std::placeholders::_1));
        }

        void RegisterResultHandler()
        {
            // 重定向到首页
            _result_route.emplace("PERMIT_LOGIN", [](std::string msg) -> std::string
                                  {
                    HttpResponse resp;
                    //0、将msg中的账号密码解析出来
                    std::string name, password;
                    Util::DistinguishUerAndPassword(msg, &name, &password);
                    //1、登录成功，为客服端设置cookie值
                    name = "user=" + name;
                    password = "pwd=" + password;
                    resp.SetHeader("Set-Cookie", name);
                    resp.SetHeader("Set-Cookie", password);
                    resp.SetVision("HTTP/1.1");
                    resp.IndexPage();
                    return resp.Serialize(); });
            // 重定向到登录
            _result_route.emplace("REGISTER_SUCCESS", [](std::string msg) -> std::string
                                  {
                    HttpResponse resp;
                    resp.SetVision("HTTP/1.1");
                    resp.LoginPage();
                    return resp.Serialize(); });
        }

        void RegisterTransactionHandler()
        {
            _trans_route.emplace("./wwwroot/login", [](std::string msg)
                                 {
            LOG_DEBUG << "RegisterTransactionHandler";
            Transaction tran;
            return tran.Login(msg); });
        }

        std::string Return404Page(std::string version)
        {
            HttpResponse error_resp;
            error_resp.SetVision(version);
            error_resp.Error_404();
            return error_resp.Serialize();
        }

        std::string ReturnLoginPage(std::string version)
        {
            HttpResponse error_resp;
            error_resp.SetVision(version);
            error_resp.LoginPage();
            return error_resp.Serialize();
        }

        // 处理Session
        bool RecognizeSession(HttpRequest &req)
        {
            static int user = 0;
            // 一、当前登录的是登录页面，创建session
            LOG_DEBUG << "----------------";
            LOG_DEBUG << req.Uri();
            if (req.Uri() == "./wwwroot/login.html")
            {

                // 1、历史没有登录过
                if (req.SessionId().empty())
                {
                    LOG_DEBUG << "历史没有登录过，进行注册session!";
                    std::string username = "user-" + std::to_string(user++);
                    _session = _session_manager.AddSession(username);
                }
                return true;
            }
            else // 二、当前是其他页面，需要进行验证。
            {
                LOG_DEBUG << "---------------1----------------";
                std::string sessionid = req.SessionId();
                if (sessionid.empty())
                    return false;
                // 1、目标ssion不存在，直接返回首页
                if (_session_manager.GetSession(sessionid) == nullptr)
                {
                    LOG_DEBUG << "---------------2----------------";
                    return false;
                }
                else // 2、存在，允许访问网站信息
                {
                    LOG_DEBUG << "---------------3----------------";
                    return true;
                }
            }
        }

        std::string HandlerRequest(std::string req_str)
        {
            // 1、反序列化请求,失败直接返回空串
            HttpRequest req;
            if (!req.Deserialize(req_str))
            {
                LOG_ERROR << "Failed to deserialize the message sent by the client.";
                return "";
            }
            // ---- 处理一下图标返回
            if (req.Uri() == "./wwwroot/favicon.ico")
            {
                return "";
            }

            // 新增功能:session, 回调处理
            Session_hander_t ses_handler = std::bind(&Http::RecognizeSession, this, std::placeholders::_1);
            bool ret = ses_handler(req);
            if (ret == false) // 没有对应的Session,回到登录页面
            {
                return ReturnLoginPage(req.Version());
            }
            else // 有对应的Session,如果有业务逻辑可以在这里进行
            {
                LOG_DEBUG << "恭喜通过了服务器对你的考验!";
            }

            // 2、如果是动态交互走动态交互的逻辑，反之走读取数据的逻辑。
            if (req.Interact())
            {
                LOG_DEBUG << "------来到了处理动态业务-------";
                // _3.1、如果找到对应路由服务，那么执行对应的函数,下面针对POST的方式来应答，后续在加GET
                if (_trans_route.count(req.Uri()))
                {
                    // 处理返回值
                    std::string ret = _trans_route[req.Uri()](req.Text());
                    if (_result_route.count(ret))
                    {
                        LOG_DEBUG << "_result_route.count(ret)";
                        // 这里传入Text是为了建立cookie
                        return _result_route[ret](req.Text());
                    }
                    else
                    {
                        // 如果没有找到目标处理字符串，说明我们之前的动态交互用户提供非法信息，返回404页面，
                        // 当然这里以后了解前端返回一个确定的原因最好。
                        LOG_DEBUG << "// 当然这里以后了解前端返回一个确定的原因最好";
                        return Return404Page(req.Version());
                    }
                }
                else
                {
                    LOG_DEBUG << "// 没找到对应的回调，进行404重定向。";
                    // 没找到对应的回调，进行404重定向。
                    return Return404Page(req.Version());
                }
            }
            {
                // _3.1、非动态交互，设置好目标文件构建应答
                HttpResponse resp;
                resp.SetTarget(req.Uri());
                resp.SetVision(req.Version());
                if (!_session.empty())
                {
                    resp.SetHeader("Cookie", _session);
                }
                // _3.2、构建应答
                resp.MakeResponse();
                // resp.Debug();
                return resp.Serialize();
            }
        }

    private:
        // 1、tcpserver会话层回调
        std::unique_ptr<TcpServerModule::TcpServer> _tcpserver;
        // 2、应用层业务回调
        std::unordered_map<std::string, Transaction_handler_t> _trans_route;
        // 3、应用层业务返回处理回调
        std::unordered_map<std::string, Result_hander_t> _result_route;
        // 4、Session用户信息管理
        SessionManager _session_manager;

        // 5、session是否添加，这个设计十分丑陋，有空修改
        std::string _session;
    };
}